package main

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
)

type httpHeaders struct {
	Method string
	Url string
	HttpVersion string

	RequestHeader map[string]string
	Body []byte
}

type responsePackages struct {
	HttpVersion string
	StatusCode uint
	StatusMsg string

	responseHeader map[string]string
	Body []byte
}

const (
	CRLF = "\r\n" // 定义行结束
	CRLF2 = "\r\n\r\n" // 定义首部报文结束符
)

func main() {
	// 监听TCP 8081端口
	listener , err := net.Listen("tcp","0.0.0.0:8081")
	if err != nil {
		panic(err)
	}

	for {
		conn,err := listener.Accept()
		if err != nil {
			continue
		}
		go worker(conn)
	}
}

func buildResponsePack(rp responsePackages)([]byte) {
	responseByte := make([]byte,0)
	// 构建状态行
	// 版本
	responseByte = append(responseByte, []byte(rp.HttpVersion+" ")...)

	// 状态码
	responseByte = append(responseByte, []byte(strconv.Itoa(int(rp.StatusCode))+" ")...)

	// 短语
	responseByte = append(responseByte, []byte(rp.StatusMsg+CRLF)...)

	// 首部信息
	for k,v := range rp.responseHeader {
		responseByte = append(responseByte, []byte(k+": ")...)
		responseByte = append(responseByte, []byte(v+CRLF)...)
	}

	// 添加响应主体
	if 0 < len(rp.Body) {
		// 新增 Content-Length
		responseByte = append(responseByte, []byte("Content-Length: "+strconv.Itoa(len(rp.Body))+"\r\n")...)
	}

	responseByte = append(responseByte, []byte(CRLF2)...)
	responseByte = append(responseByte, rp.Body...)

	fmt.Printf("生成响应报文\n%s\n\n\n",string(responseByte))
	return responseByte
}

func readReuestMessage(conn net.Conn) (httpHeaders,error) {
	maxLen := 8182
	headBuf := make([]byte,maxLen) // 申请内存用于接收报文

	var httpHeader httpHeaders

	readSize := 0
	// 每次读取一个字节数据
	for i:=1;i<=maxLen;i++ {
		n,err := conn.Read(headBuf[readSize:i])
		if err != nil {
			fmt.Println("error: " , err)
			return httpHeader ,err
		}
		readSize = readSize + n

		// 判断首部报文是否结束了
		if len(CRLF2) <= len(string(headBuf[:readSize])) {
			if string(headBuf[readSize-len(CRLF2):readSize]) == CRLF2 {
				break
			}
		}
	}

	// 判断首部报文是否超过允许接收长度
	if maxLen <= readSize {
		fmt.Println("超过允许接收的最大长度")
		return httpHeader , errors.New("超过允许接收的最大长度")
	}

	// 声明 httpHeaders
	httpHeader.RequestHeader = make(map[string]string,1)

	// 将获取的首部数据打印出来
	for k,v := range strings.Split(string(headBuf[:readSize-len(CRLF)]),CRLF) {
		if v == "" {
			continue
		}

		if 0 == k {
			// 请求行
			header := strings.Split(v," ")
			httpHeader.Method = header[0]
			httpHeader.Url = header[1]
			httpHeader.HttpVersion = header[2]
		} else {
			// 请求头
			requestLine := strings.Split(v,": ")
			httpHeader.RequestHeader[requestLine[0]] = requestLine[1]
		}
	}

	// 获取报文主体数据
	bodyLen , ok := httpHeader.RequestHeader["Content-Length"];if ok {
		bodyLenInt , err := strconv.Atoi(bodyLen)
		if err != nil {
			fmt.Println("Content-Length值转化失败")
			return httpHeader,err
		}

		// 从管道中取 Content-Length 长度的数据
		bodyBuf := make([]byte,bodyLenInt)
		recvLen := 0
		for recvLen < bodyLenInt {
			n , err := conn.Read(bodyBuf[recvLen:bodyLenInt])
			if err != nil {
				fmt.Println("read error " , err)
				return httpHeader,err
			}
			recvLen += n
		}
		httpHeader.Body = bodyBuf[:]
	}

	fmt.Printf("获取请求报文: \n请求方法:%s\nURL:%s\n协议版本:%s\n请求头:%v\n报文主体:%s\n\n\n", httpHeader.Method , httpHeader.Url,httpHeader.HttpVersion , httpHeader.RequestHeader , httpHeader.Body)
	return httpHeader,nil
}

func worker(conn net.Conn) {
	defer conn.Close()

	// 拆解请求报文
	httpHeader , err := readReuestMessage(conn)
	if err != nil {
		return
	}

	var rp responsePackages
	rp.responseHeader = make(map[string]string,1)

	// 接收响应报文
	var repBuf []byte

	// 分析URL
	if httpHeader.Url == "/pdudo" {

		// 返回 hello world
		rp.HttpVersion = "HTTP/1.1"
		rp.StatusCode = 200
		rp.StatusMsg = "OK"
		rp.responseHeader["User-Agent"] = "pdudo_golang"
		rp.Body = []byte("<h1>hello world</h1>")
		repBuf = buildResponsePack(rp)
	} else {
		// 返回 跳转报文
		rp.HttpVersion = "HTTP/1.1"
		rp.StatusCode = 301
		rp.StatusMsg = "Moved Permanently"
		rp.responseHeader["Location"] = "https://juejin.cn/"
		repBuf = buildResponsePack(rp)
	}

	// 将repBuf返回给客户端
	sendLen := 0
	for sendLen < len(repBuf) {
		n ,err := conn.Write(repBuf[sendLen:])
		if err != nil {
			fmt.Println(err)
			return
		}
		sendLen += n
	}
}